﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;

namespace SickBox
{
    class Program
    {
        private static String _privateKey = "";
        private static IPHostEntry _ipHost;
        private static IPAddress _ipAddr;

        static void Main(string[] args)
        {
            init();
            PrintCommands();

            while (true)
            {
                Boolean cmdIsExit = ParseCommand(Console.ReadLine());
                if (cmdIsExit)
                {
                    break;
                }
            }
            Console.WriteLine("SickBox terminated. Press any key...");
            Console.ReadLine();
        }

        /// <summary>
        /// Will parse the given line for a command.
        /// </summary>
        /// <param name="cmd">Command to parse and process.</param>
        /// <returns>Returns true ONLY if "exit" is parsed, otherwise false.</returns>
        static Boolean ParseCommand(String cmd)
        {
            switch (cmd)
            {
                case "get":
                    GetFiles();
                    return false;
                case "push":
                    PushFiles();
                    return false;
                case "pushsingle":
                    PushSingle();
                    return false;
                case "genkeys":
                    GenerateKeys();
                    return false;
                case "help":
                    PrintCommands();
                    return false;
                case "exit":
                    exit();
                    return true;
                default:
                    Console.WriteLine("Seems an unknown command was entered. Please try again.");
                    return false;
            }
        }

        static void GetFiles()
        {
            if (File.Exists("public.sb"))
            {

            }
            else
            {
                Console.WriteLine("No public key found.");
                Console.WriteLine("Please generate a keyset first with the 'genkeys' command");
                return;
            }
        }

        static void PushFiles()
        {

            StringBuilder sb = new StringBuilder();
            if (File.Exists("partner.sb"))
            {
                Console.WriteLine("Partners Public Key Found!");
                // TODO: Figure out why the ConfigurationManager won't be recognized as a class.
                if (ConfigurationSettings.AppSettings["push_dir"] != null || ConfigurationSettings.AppSettings["push_dir"].Length != 0)
                {
                    if (ConfigurationSettings.AppSettings["ip_partner"] != null || ConfigurationSettings.AppSettings["ip_partner"].Length != 0)
                    {
                        IPEndPoint partnerEnd = Util.CreateIPEndPoint(ConfigurationSettings.AppSettings["ip_partner"]);
                        String push_dir = ConfigurationSettings.AppSettings["push_dir"];
                        String[] key = File.ReadAllLines("partner.sb", Encoding.UTF8);
                        foreach (String keyString in key)
                        {
                            sb.Append(keyString);
                        }
                        Console.WriteLine("Encryption Started...");
                        Console.WriteLine("---------------------------------------------");
                        List<Dictionary<String, byte[]>> data = new List<Dictionary<String, byte[]>>();

                        foreach (String path in Directory.GetFiles(push_dir, "*.*", SearchOption.AllDirectories))
                        {
                            if (Path.GetFileName(path).Equals("partner.sb"))
                            {
                                continue;
                            }
                            else
                            {
                                Dictionary<String, byte[]> encryptedFile = path.Encrypt(sb.ToString());
                                data.Add(encryptedFile);
                            }
                        }
                        Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        Console.Write("Connecting to Partner...");
                        client.Connect(partnerEnd);
                        Console.WriteLine("Connected");
                        Console.WriteLine("Communicating with Partner...");

                        // TODO: Establish Protocol

                        foreach (Dictionary<String, byte[]> dic in data)
                        {
                            foreach (KeyValuePair<String, byte[]> pair in dic)
                            {
                                // TODO: Put in Arrays to Send.

                                Console.Write("Sending '" + pair.Key + "'...");
                                client.SendFile(pair.Key, null, null, TransmitFileOptions.ReuseSocket);
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine("The variable 'ip_partner' seems to be missing from the App.config file or the string is missing.");
                        Console.WriteLine("This needs to be resolved before you can push files to your partner.");
                        return;
                    }
                }
                else
                {
                    Console.WriteLine("The variable 'push_dir' seems to be missing from the App.config file or the path is missing.");
                    Console.WriteLine("This needs to be resolved before you can push files to your partner.");
                    return;
                }

            }
            else
            {
                Console.WriteLine("No public key from partner found. (partner.sb)");
                Console.WriteLine("Please request a public key from the receiver first and place it in: ");
                Console.WriteLine(Directory.GetCurrentDirectory());
                return;
            }
        }

        static void PushSingle()
        {
            Console.WriteLine("Please write the name of the file in the current folder with extension you wish to transfer (case sensitive)");
            String file = Console.ReadLine();
        }

        /// <summary>
        /// See <see cref="Util.GenerateKeyPair()"/> for more information
        /// </summary>
        private static void GenerateKeys()
        {
            _privateKey = Util.GenerateKeyPair();
        }

        private static void init()
        {
            Console.Title = "SickBox";
            Console.SetWindowSize(100, 40);
            Console.WriteLine("Setting Up...");
            Console.Write("Getting Host...");
            _ipHost = Dns.GetHostEntry(Dns.GetHostName());
            Console.WriteLine("Done");
            Console.Write("Getting IP Address...");
            _ipAddr = _ipHost.AddressList[0];
            Console.WriteLine("Done");
            Console.WriteLine("Done");
            Console.WriteLine();
            Console.WriteLine("Welcome to SickBox");
            Console.WriteLine("For bitches that are paranoid and just generally assholes");
            Console.WriteLine("---------------------------------------------");
            Console.WriteLine("Your Info is:");
            Console.WriteLine("IP Address: " + _ipAddr.ToString());
            Console.WriteLine("---------------------------------------------");
        }

        private static void exit()
        {
            if (File.Exists("public.sb"))
            {
                Console.WriteLine("Public Key Found. Deleting Public Key!");
                File.Delete("public.sb");
            }
        }

        private static void PrintCommands()
        {
            Dictionary<String, String> _commands;

            _commands = new Dictionary<String, String>();
            _commands.Add("get", "Try and get files from partner");
            _commands.Add("push", "Try to push all files to partner in current folder");
            _commands.Add("pushsingle", "Try to push a specific file in current folder");
            _commands.Add("genkeys", "Generates public/private keypair. Keys become invalid when program terminates.");
            _commands.Add("help", "Will show this list of commands");
            _commands.Add("exit", "Exits SickBox");

            Console.WriteLine("Available Commands:");
            foreach (KeyValuePair<String, String> s in _commands)
            {
                Console.WriteLine("\t- " + s.Key + ": " + s.Value);
            }
            Console.WriteLine();
        }

    }
}